home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Internet Surfer 2.0
/
Internet Surfer 2.0 (Wayzata Technology) (1996).iso
/
pc
/
text
/
mac
/
faqs.223
< prev
next >
Wrap
Text File
|
1996-02-12
|
25KB
|
667 lines
Frequently Asked Questions (FAQS);faqs.223
A: In general, you cannot. You must provide a version of that
other function which accepts a va_list pointer, as does vfprintf
in the example above. If the arguments must be passed directly
as actual arguments (not indirectly through a va_list pointer)
to another function which is itself variadic (for which you do
not have the option of creating an alternate, va_list-accepting
version) no portable solution is possible. (The problem can be
solved by resorting to machine-specific assembly language.)
7.5: How can I call a function with an argument list built up at run
time?
A: There is no guaranteed or portable way to do this. If you're
curious, ask this list's editor, who has a few wacky ideas you
could try... (See also question 16.10.)
Section 8. Boolean Expressions and Variables
8.1: What is the right type to use for boolean values in C? Why
isn't it a standard type? Should #defines or enums be used for
the true and false values?
A: C does not provide a standard boolean type, because picking one
involves a space/time tradeoff which is best decided by the
programmer. (Using an int for a boolean may be faster, while
using char may save data space.)
The choice between #defines and enums is arbitrary and not
terribly interesting (see also question 9.1). Use any of
#define TRUE 1 #define YES 1
#define FALSE 0 #define NO 0
enum bool {false, true}; enum bool {no, yes};
or use raw 1 and 0, as long as you are consistent within one
program or project. (An enum may be preferable if your debugger
expands enum values when examining variables.)
Some people prefer variants like
#define TRUE (1==1)
#define FALSE (!TRUE)
or define "helper" macros such as
#define Istrue(e) ((e) != 0)
These don't buy anything (see question 8.2 below; see also
question 1.6).
8.2: Isn't #defining TRUE to be 1 dangerous, since any nonzero value
is considered "true" in C? What if a built-in boolean or
relational operator "returns" something other than 1?
A: It is true (sic) that any nonzero value is considered true in C,
but this applies only "on input", i.e. where a boolean value is
expected. When a boolean value is generated by a built-in
operator, it is guaranteed to be 1 or 0. Therefore, the test
if((a == b) == TRUE)
will work as expected (as long as TRUE is 1), but it is
obviously silly. In general, explicit tests against TRUE and
FALSE are undesirable, because some library functions (notably
isupper, isalpha, etc.) return, on success, a nonzero value
which is _not_ necessarily 1. (Besides, if you believe that
"if((a == b) == TRUE)" is an improvement over "if(a == b)", why
stop there? Why not use "if(((a == b) == TRUE) == TRUE)"?) A
good rule of thumb is to use TRUE and FALSE (or the like) only
for assignment to a Boolean variable, or as the return value
from a Boolean function, never in a comparison.
The preprocessor macros TRUE and FALSE (and, of course, NULL)
are used for code readability, not because the underlying values
might ever change. (See also question 1.7.)
References: K&R I Sec. 2.7 p. 41; K&R II Sec. 2.6 p. 42,
Sec. A7.4.7 p. 204, Sec. A7.9 p. 206; ANSI Secs. 3.3.3.3, 3.3.8,
3.3.9, 3.3.13, 3.3.14, 3.3.15, 3.6.4.1, 3.6.5; Achilles and the
Tortoise.
Section 9. Structs, Enums, and Unions
9.1: What is the difference between an enum and a series of
preprocessor #defines?
A: At the present time, there is little difference. Although many
people might have wished otherwise, the ANSI standard says that
enumerations may be freely intermixed with integral types,
without errors. (If such intermixing were disallowed without
explicit casts, judicious use of enums could catch certain
programming errors.)
The primary advantages of enums are that the numeric values are
automatically assigned, and that a debugger may be able to
display the symbolic values when enum variables are examined.
(A compiler may also generate nonfatal warnings when enums and
ints are indiscriminately mixed, since doing so can still be
considered bad style even though it is not strictly illegal). A
disadvantage is that the programmer has little control over the
size (or over those nonfatal warnings).
References: K&R II Sec. 2.3 p. 39, Sec. A4.2 p. 196; H&S
Sec. 5.5 p. 100; ANSI Secs. 3.1.2.5, 3.5.2, 3.5.2.2 .
9.2: I heard that structures could be assigned to variables and
passed to and from functions, but K&R I says not.
A: What K&R I said was that the restrictions on struct operations
would be lifted in a forthcoming version of the compiler, and in
fact struct assignment and passing were fully functional in
Ritchie's compiler even as K&R I was being published. Although
a few early C compilers lacked struct assignment, all modern
compilers support it, and it is part of the ANSI C standard, so
there should be no reluctance to use it.
References: K&R I Sec. 6.2 p. 121; K&R II Sec. 6.2 p. 129; H&S
Sec. 5.6.2 p. 103; ANSI Secs. 3.1.2.5, 3.2.2.1, 3.3.16 .
9.3: How does struct passing and returning work?
A: When structures are passed as arguments to functions, the entire
struct is typically pushed on the stack, using as many words as
are required. (Programmers often choose to use pointers to
structures instead, precisely to avoid this overhead.)
Structures are often returned from functions in a location
pointed to by an extra, compiler-supplied "hidden" argument to
the function. Some older compilers used a special, static
location for structure returns, although this made struct-valued
functions nonreentrant, which ANSI C disallows.
Reference: ANSI Sec. 2.2.3 p. 13.
9.4: The following program works correctly, but it dumps core after
it finishes. Why?
struct list
{
char *item;
struct list *next;
}
/* Here is the main program. */
main(argc, argv)
...
A: A missing semicolon causes the compiler to believe that main
returns a structure. (The connection is hard to see because of
the intervening comment.) Since struct-valued functions are
usually implemented by adding a hidden return pointer, the
generated code for main() tries to accept three arguments,
although only two are passed (in this case, by the C start-up
code). See also question 17.15.
Reference: CT&P Sec. 2.3 pp. 21-2.
9.5: Why can't you compare structs?
A: There is no reasonable way for a compiler to implement struct
comparison which is consistent with C's low-level flavor. A
byte-by-byte comparison could be invalidated by random bits
present in unused "holes" in the structure (such padding is used
to keep the alignment of later fields correct). A field-by-
field comparison would require unacceptable amounts of
repetitive, in-line code for large structures.
If you want to compare two structures, you must write your own
function to do so. C++ would let you arrange for the ==
operator to map to your function.
References: K&R II Sec. 6.2 p. 129; H&S Sec. 5.6.2 p. 103; ANSI
Rationale Sec. 3.3.9 p. 47.
9.6: I came across some code that declared a structure like this:
struct name
{
int namelen;
char name[1];
};
and then did some tricky allocation to make the name array act
like it had several elements. Is this legal and/or portable?
A: This technique is popular, although Dennis Ritchie has called it
"unwarranted chumminess with the compiler." The ANSI C standard
allows it only implicitly. It seems to be portable to all known
implementations. (Compilers which check array bounds carefully
might issue warnings.)
References: ANSI Rationale Sec. 3.5.4.2 pp. 54-5.
9.7: How can I determine the byte offset of a field within a
structure?
A: ANSI C defines the offsetof macro, which should be used if
available; see <stddef.h>. If you don't have it, a suggested
implementation is
#define offsetof(type, mem) ((size_t) \
((char *)&((type *) 0)->mem - (char *)((type *) 0)))
This implementation is not 100% portable; some compilers may
legitimately refuse to accept it.
See the next question for a usage hint.
References: ANSI Sec. 4.1.5 , Rationale Sec. 3.5.4.2 p. 55.
9.8: How can I access structure fields by name at run time?
A: Build a table of names and offsets, using the offsetof() macro.
The offset of field b in struct a is
offsetb = offsetof(struct a, b)
If structp is a pointer to an instance of this structure, and b
is an int field with offset as computed above, b's value can be
set indirectly with
*(int *)((char *)structp + offsetb) = value;
9.9: Why does sizeof report a larger size than I expect for a
structure type, as if there was padding at the end?
A: Structures may have this padding (as well as internal padding;
see also question 9.5), so that alignment properties will be
preserved when an array of contiguous structures is allocated.
9.10: My compiler is leaving holes in structures, which is wasting
space and preventing "binary" I/O to external data files. Can I
turn off the padding, or otherwise control the alignment of
structs?
A: Your compiler may provide an extension to give you this control
(perhaps a #pragma), but there is no standard method. See also
question 17.2.
9.11: Can I initialize unions?
A: ANSI Standard C allows an initializer for the first member of a
union. There is no standard way of initializing the other
members (nor, under a pre-ANSI compiler, is there generally any
way of initializing any of them).
Section 10. Declarations
10.1: How do you decide which integer type to use?
A: If you might need large values (above 32767 or below -32767),
use long. Otherwise, if space is very important (there are
large arrays or many structures), use short. Otherwise, use
int. If well-defined overflow characteristics are important
and/or negative values are not, use the corresponding unsigned
types. (But beware of mixing signed and unsigned in
expressions.) Similar arguments apply when deciding between
float and double.
Although char or unsigned char can be used as a "tiny" int type,
doing so is often more trouble than it's worth, due to
unpredictable sign extension and increased code size.
These rules obviously don't apply if the address of a variable
is taken and must have a particular type.
If for some reason you need to declare something with an _exact_
size (usually the only good reason for doing so is when
attempting to conform to some externally-imposed storage layout,
but see question 17.2), be sure to encapsulate the choice behind
an appropriate typedef.
10.2: I can't seem to define a linked list successfully. I tried
typedef struct
{
char *item;
NODEPTR next;
} *NODEPTR;
but the compiler gave me error messages. Can't a struct in C
contain a pointer to itself?
A: Structs in C can certainly contain pointers to themselves; the
discussion and example in section 6.5 of K&R make this clear.
The problem with this example is that the NODEPTR typedef is not
complete at the point where the "next" field is declared. To
fix it, first give the structure a tag ("struct node"). Then,
declare the "next" field as "struct node *next;", and/or move
the typedef declaration wholly before or wholly after the struct
declaration. One fixed version would be
struct node
{
char *item;
struct node *next;
};
typedef struct node *NODEPTR;
, and there are at least three other equivalently correct ways
of arranging it.
A similar problem, with a similar solution, can arise when
attempting to declare a pair of typedef'ed mutually recursive
structures.
References: K&R I Sec. 6.5 p. 101; K&R II Sec. 6.5 p. 139; H&S
Sec. 5.6.1 p. 102; ANSI Sec. 3.5.2.3 .
10.3: How do I declare an array of pointers to functions returning
pointers to functions returning pointers to characters?
A: This question can be answered in at least three ways (all
declare the hypothetical array with 5 elements):
1. char *(*(*a[5])())();
2. Build the declaration up in stages, using typedefs:
typedef char *pc; /* pointer to char */
typedef pc fpc(); /* function returning pointer to char */
typedef fpc *pfpc; /* pointer to above */
typedef pfpc fpfpc(); /* function returning... */
typedef fpfpc *pfpfpc; /* pointer to... */
pfpfpc a[5]; /* array of... */
3. Use the cdecl program, which turns English into C and vice
versa:
cdecl> declare a as array 5 of pointer to function returning
pointer to function returning pointer to char
char *(*(*a[5])())()
cdecl can also explain complicated declarations, help with
casts, and indicate which set of parentheses the arguments
go in (for complicated function definitions, like the
above). Versions of cdecl are in volume 14 of
comp.sources.unix (see question 17.8) and K&R II.
Any good book on C should explain how to read these complicated
C declarations "inside out" to understand them ("declaration
mimics use").
References: K&R II Sec. 5.12 p. 122; H&S Sec. 5.10.1 p. 116.
10.4: I'm building a state machine with a bunch of functions, one for
each state. I want to implement state transitions by having
each function return a pointer to the next state function. I
find a limitation in C's declaration mechanism: there's no way
to declare these functions as returning a pointer to a function
returning a pointer to a function returning a pointer to a
function...
A: You can't do it directly. Either have the function return a
generic function pointer type, and apply a cast before calling
through it; or have it return a structure containing only a
pointer to a function returning that structure.
10.5: What's the best way to declare and define global variables?
A: First, though there can be many _declarations_ (and in many
translation units) of a single "global" (strictly speaking,
"external") variable (or function), there must be exactly one
_definition_. (The definition is the declaration that actually
allocates space, and provides an initialization value, if any.)
It is best to place the definition in some central (to the
program, or to the module) .c file, with an external declaration
in a header (".h") file, which is #included wherever the
declaration is needed. The .c file containing the definition
should also #include the header file containing the external
declaration, so that the compiler can check that the
declarations match.
This rule promotes a high degree of portability, and is
consistent with the requirements of the ANSI C Standard. Note
that Unix compilers and linkers typically use a "common model"
which allows multiple (uninitialized) definitions. A few very
odd systems may require an explicit initializer to distinguish a
definition from an external declaration.
It is possible to use preprocessor tricks to arrange that the
declaration need only be typed once, in the header file, and
"turned into" a definition, during exactly one #inclusion, via a
special #define.
References: K&R I Sec. 4.5 pp. 76-7; K&R II Sec. 4.4 pp. 80-1;
ANSI Sec. 3.1.2.2 (esp. Rationale), Secs. 3.7, 3.7.2,
Sec. F.5.11 .
10.6: I finally figured out the syntax for declaring pointers to
functions, but now how do I initialize one?
A: Use something like
extern int func();
int (*fp)() = func;
When the name of a function appears in an expression but is not
being called (i.e. is not followed by a "("), it "decays" into a
pointer (i.e. it has its address implicitly taken), much as an
array name does.
An explicit extern declaration for the function is normally
needed, since implicit external function declaration does not
happen in this case (again, because the function name is not
followed by a "(").
10.7: I've seen different methods used for calling through pointers to
functions. What's the story?
A: Originally, a pointer to a function had to be "turned into" a
"real" function, with the * operator (and an extra pair of
parentheses, to keep the precedence straight), before calling:
int r, func(), (*fp)() = func;
r = (*fp)();
It can also be argued that functions are always called through
pointers, but that "real" functions decay implicitly into
pointers (in expressions, as they do in initializations) and so
cause no trouble. This reasoning, made widespread through pcc
and adopted in the ANSI standard, means that
r = fp();
is legal and works correctly, whether fp is a function or a
pointer to one. (The usage has always been unambiguous; there
is nothing you ever could have done with a function pointer
followed by an argument list except call through it.) An
explicit * is harmless, and still allowed (and recommended, if
portability to older compilers is important).
References: ANSI Sec. 3.3.2.2 p. 41, Rationale p. 41.
Section 11. Stdio
11.1: Why doesn't this code:
char c;
while((c = getchar()) != EOF)...
work?
A: For one thing, the variable to hold getchar's return value must
be an int. getchar can return all possible character values, as
well as EOF. By passing getchar's return value through a char,
either a normal character might be misinterpreted as EOF, or the
EOF might be altered and so never seen.
References: CT&P Sec. 5.1 p. 70.
11.2: Why does errno contain ENOTTY after a call to printf?
A: Many implementations of the stdio package adjust their behavior
slightly if stdout is a terminal. To make the determination,
these implementations perform an operation which fails (with
ENOTTY) if stdout is not a terminal. Although the output
operation goes on to complete successfully, errno still contains
ENOTTY.
Reference: CT&P Sec. 5.4 p. 73.
11.3: My program's prompts and intermediate output don't always show
up on the screen, especially when I pipe the output through
another program.
A: It is best to use an explicit fflush(stdout) whenever output
should definitely be visible. Several mechanisms attempt to
perform the fflush for you, at the "right time," but they tend
to apply only when stdout is a terminal. (See question 11.2.)
11.4: When I read from the keyboard with scanf, it seems to hang until
I type one extra line of input.
A: scanf was designed for free-format input, which is seldom what
you want when reading from the keyboard. In particular, "\n" in
a format string does _not_ mean to expect a newline, but rather
to read and discard characters as long as each is a whitespace
character.
It is usually better to use fgets to read a whole line, and
then use sscanf or other string functions to pick apart the line
buffer. If you do use sscanf, don't forget to check the return
value to make sure that the expected number of items were found.
11.5: How can I flush pending input so that a user's typeahead isn't
read at the next prompt? Will fflush(stdin) work?
A: fflush is defined only for output streams. Since its definition
of "flush" is to complete the writing of buffered characters
(not to discard them), discarding unread input would not be an
analogous meaning for fflush on input streams. There is no
standard way to discard unread characters from a stdio input
buffer, nor would such a way be sufficient; unread characters
can also accumulate in other, OS-level input buffers.
11.6: Once I've used freopen, how can I get the original stdout (or
stdin) back?
A: If you need to switch back and forth, the best all-around
solution is not to use freopen in the first place. Try using
your own explicit output (or input) stream variable, which you
can reassign at will, while leaving the original stdout (or
stdin) undisturbed.
11.7: How can I recover the file name given an open file descriptor?
A: This problem is, in general, insoluble. Under Unix, for
instance, a scan of the entire disk, (perhaps requiring special
permissions) would theoretically be required, and would fail if
the file descriptor was a pipe or referred to a deleted file
(and could give a misleading answer for a file with multiple
links). It is best to remember the names of files yourself when
you open them (perhaps with a wrapper function around fopen).
Section 12. Library Subroutines
12.1: I'm trying to sort an array of strings with qsort, using strcmp
as the comparison function, but it's not working.
A: By "array of strings" you probably mean "array of pointers to
char." The arguments to qsort's comparison function are
pointers to the objects being sorted, in this case, pointers to
pointers to char. (strcmp, of course, accepts simple pointers
to char.)
The comparison routine's arguments are expressed as "generic
pointers," void * or char *. They must be converted back to
what they "really are" (char **) and dereferenced, yielding
char *'s which can be usefully compared. Write a comparison
function like this:
int pstrcmp(p1, p2) /* compare strings through pointers */
char *p1, *p2; /* void * for ANSI C */
{
return strcmp(*(char **)p1, *(char **)p2);
}
12.2: Now I'm trying to sort an array of structures with qsort. My
comparison routine takes pointers to structures, but the
compiler complains that the function is of the wrong type for
qsort. How can I cast the function pointer to shut off the
warning?
A: The conversions must be in the comparison function, which must
be declared as accepting "generic pointers" (void * or char *)
as discussed above.
12.3: How can I convert numbers to strings (the opposite of atoi)? Is
there an itoa function?
A: Just use sprintf. (You'll have to allocate space for the result
somewhere anyway; see questions 3.1 and 3.2. Don't worry that
sprintf may be overkill, potentially wasting run time or code
space; it works well in practice.)
References: K&R I Sec. 3.6 p. 60; K&R II Sec. 3.6 p. 64.
12.4: How can I get the time of day in a C program?
A: Just use the time, ctime, and/or localtime functions. (These
routines have been around for years, and are in the ANSI
standard.) Here is a simple example:
#include <stdio.h>
#include <time.h>
main()
{
time_t now;
time(&now);
printf("It's %.24s.\n", ctime(&now));
exit(0);
}
References: ANSI Sec. 4.12 .
12.5: I know that the library routine localtime will convert a time_t
into a broken-down struct tm, and that ctime will convert a
time_t to a printable string. How can I perform the inverse
operations of converting a struct tm or a string into a time_t?
A: ANSI C specifies a library routine, mktime, which converts a
struct tm to a time_t. Several public-domain versions of this
routine are available in case your compiler does not support it
yet.
Converting a string to a time_t is harder, because of the wide
variety of date and time formats which should be parsed.
Public-domain routines have been written for performing this
function (see, for example, the file partime.c, widely
distributed with the RCS package), but they are less likely to
become standardized.
References: K&R II Sec. B10 p. 256; H&S Sec. 20.4 p. 361; ANSI
Sec. 4.12.2.3 .
12.6: I need a random number generator.
A: The standard C library has one: rand(). The implementation on
your system may not be perfect, but writing a better one isn't
necessarily easy, either.
References: ANSI Sec. 4.10.2.1 p. 154; Knuth Vol. 2 Chap. 3
pp. 1-177.
12.7: Each time I run my program, I get the same sequence of numbers
back from rand().
A: You can call srand() to seed the pseudo-random number generator
with a more random initial value. Popular random initial seeds
are the time of day, or the elapsed time before the user presses
a key.
References: ANSI Sec. 4.10.2.2 p. 154.
12.8: I need a random true/false value, so I'm taking rand() % 2, but
it's just alternating 0, 1, 0, 1, 0...
A: Poor pseudorandom number generators (such as the ones
unfortunately supplied with some systems) are not very random in
the low-order bits. Try using the higher-order bits.
12.9-13: I'm trying to port this old A: These routines are variously
program. Why do I get obsolete; you should
"undefined external" errors instead:
for:
12.9: index? A: use strchr.
12.10: rindex? A: use strrchr.
12.11: bcopy? A: use memmove, after
interchanging the first and
second arguments (see also
question 5.10).
12.12: bcmp? A: use memcmp.
12.13: bzero? A: use memset, with a second
argument of 0.
12.14: How can I execute a command with system() and read its output
into a program?
A: Unix and some other systems provide a popen() routine, which
sets up a stdio stream on a pipe connected to the process
running a command, so that the output can be read (or the input
supplied).
12.15: How can I read a directory in a C program?
A: See if you can use the opendir() and readdir() routines, which
are available on most Unix systems. Implementations also exist
for MS-DOS, VMS, and other systems. (MS-DOS also has FINDFIRST
and FINDNEXT routines which do essentially the same thing.)